package com.wangcanxuan.www.proxy;

import com.wangcanxuan.www.annotation.Transaction;
import com.wangcanxuan.www.exception.ServiceException;
import com.wangcanxuan.www.util.LoggerUtil;
import com.wangcanxuan.www.util.TransactionUtil;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.util.HashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Logger;

/**
 * service层代理类
 *
 * @author HHLJ
 * @date 2022/04/05
 */
public class ServiceProxy implements InvocationHandler {

    /**
     * 日志记录器
     */
    private static final Logger logger = LoggerUtil.getLogger();


    /**
     * 代理目标
     */
    private Object target;

    /**
     * 代理对象
     */
    private Object proxy;

    /**
     * 该map相当于一个对本代理类的缓存，也算是一种单例模式，防止生成过多的代理类
     * 假如我多次调用被代理的类，只用创建第一次代理对象，在以后调用的时候会从map集合里
     * 拿出对应我被代理类的class对象的FunctionProxy对象
     * 使用ReentrantLock保证并发情况下的并发安全，将返回写在try-finally外减小锁的粒度
     */
    private static final HashMap<Class<?>, ServiceProxy> INVOCATION_HANDLER_HASH_MAP = new HashMap<>();

    /**
     * 锁
     */
    private static final Lock LOCK = new ReentrantLock();

    /**
     * 创建代理对象
     * 代理对象proxy的类型是com.sun.proxy.$Proxy0
     *
     * @param proxyClazz 代理clazz
     * @return {@link T}
     */
    public static <T> T getProxyInstance(Class<T> proxyClazz) {
        // 从集合中获取invocationHandler，即FunctionProxy对象
        ServiceProxy invocationHandler = INVOCATION_HANDLER_HASH_MAP.get(proxyClazz);
        LOCK.lock();
        try {
            // 如果invocationHandler为空，为其初始化
            if (invocationHandler == null) {
                invocationHandler = new ServiceProxy();
                try {
                    T tar = proxyClazz.newInstance();
                    invocationHandler.setTarget(tar);
                    invocationHandler.setProxy(Proxy.newProxyInstance(tar.getClass().getClassLoader(), tar.getClass().getInterfaces(), invocationHandler));
                } catch (InstantiationException | IllegalAccessException e) {
                    e.printStackTrace();
                }
                // 在map中put被代理类的类对象和代理类的对象即invocationHandler
                INVOCATION_HANDLER_HASH_MAP.put(proxyClazz, invocationHandler);
            }
        } finally {
            LOCK.unlock();
        }
        return (T) invocationHandler.getProxy();
    }


    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result;
        // 如果接口方法有Transaction注解，根据返回类型调用封装好的更新操作
        if (method.isAnnotationPresent(Transaction.class)) {
            Connection transactionConnection = TransactionUtil.getTransactionConnection();
            try {
                // 开启事务
                TransactionUtil.transactionBegin(transactionConnection);
                logger.info("事务开启");
                result = method.invoke(target, args);
                // 提交事务
                TransactionUtil.transactionCommit(transactionConnection);
                logger.info("事务提交");
                return result;
            } catch (Exception e) {
                // 回滚事务
                e.printStackTrace();
                TransactionUtil.transactionRollback(transactionConnection);
                logger.severe("事务回滚" + e.getCause());
                throw new ServiceException("业务异常！");
            } finally {
                TransactionUtil.transactionEnd(transactionConnection);
                logger.info("事务关闭");
                TransactionUtil.returnTransactionConnection();
            }
        }
        return method.invoke(target, args);
    }

    public Object getTarget() {
        return target;
    }

    public void setTarget(Object target) {
        this.target = target;
    }

    public Object getProxy() {
        return proxy;
    }

    public void setProxy(Object proxy) {
        this.proxy = proxy;
    }
}
